﻿//Author:Michal Sporna
//License: https://operationsneakpeek.codeplex.com/license

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Levels
{
    /// <summary>
    /// represents 1 map
    /// All details are loaded from serilized json
    /// </summary>
    public class Map
    {
        #region properties
        //map name which is actually ID
        public string Name;
        //title -in game title
        public string Title;
        //all map cells
        public List<MapCell> MapCells; //list of map's cells
        //current cell grid
        public MapCell CurrentMapCell;
        //list of available obstacles
        public List<Util.Classes.Obstacle> AvailableObstacles;
        //list of available items
        public List<Util.Classes.Item> AvailableItems;
        //list of bombs
        public List<Util.Classes.Bomb> AvailableBombs;
        //NPCs
        public List<Characters.NPC> NPCs;

        //word propositions 
        public List<Util.Classes.WordProposition> WordPropositions;
        //letter placenents
        public List<LetterPlacement> LetterPlacements;
        //SECRET WORD:
        public World.Classes.SecretWord ChosenSecretWord;
        //words found
        public int LettersFound;
        //bomb found (if hidden)
        public bool BombFound;
        //bom defused 
        public bool BombDefused;
        //reward awraded for collecting all letters
        public bool RewardAwardedForAllLetters;
        //reward awarded for defusing bomb
        public bool RewardAwardedForDefusingBomb;
        //number of wild guesses of the password (if player did not collected the letter and laid out the pass, then he can try to guess)
        public int WildPasswordGuesses;

        //flags are set if stars are awarded for all letters, and the bomb is defused
        public bool StarsAwardedForAllLetters;
        public bool StarsAwardedForBombDefused;
        public bool StarsAwardedForSolvingSecretWord;
        //points for the map
        public int Points;

        #region POINTS
        public int PointsForFiding1Letter;
        public int PointsForFindingAllLetters;
        public int PointsForDefusingBomb;
        public int PointsForGuessingPassword;
        public int PointsForDefusingBomb1; //try 1
        public int PointsForDefusingBomb2; //try 2
        public int PointsForDefusingBomb3; //try 3



        #endregion

        //default number of wild password guesses for the map
        public int DefaultWildPassGuessesNumber;


        #region flags
        //if the map is current map, set to true
        public bool IsCurrentMap { get; set; }
        public int AvatarOutOfTheWaterCounter { get; set; } //count how many times I think the avatar should get out of the water
        #endregion

        #region public fields
        public List<NPCInfo> NPCInfo; //info about from where get npc for this map

        #endregion

        //helpers:
        public List<Util.Classes.WordProposition> WordProps;

        #endregion



        #region public methods

        /// <summary>
        /// default constructor
        /// </summary>
        public Map()
        {
            try
            {
                this.Name = "THESURVIVALISTMAP";
                this.Title = "THE MAP";
                this.MapCells = new List<MapCell>();
                this.CurrentMapCell = null;
                IsCurrentMap = false;
                AvailableBombs = new List<Util.Classes.Bomb>();
                AvailableItems = new List<Util.Classes.Item>();
                AvailableObstacles = new List<Util.Classes.Obstacle>();
                NPCs = new List<Characters.NPC>();
                WordProps = new List<Util.Classes.WordProposition>();

           
                
                 WordPropositions=null;
                 LetterPlacements = new List<LetterPlacement>();
                 ChosenSecretWord = null;
                LettersFound=0;
                BombFound=false;
                BombDefused=false;
                RewardAwardedForAllLetters=false;
                RewardAwardedForDefusingBomb=false;
                WildPasswordGuesses=0;

                PointsForFiding1Letter=0;
                PointsForFindingAllLetters=0;
                PointsForDefusingBomb=0;
                PointsForGuessingPassword=0;
                PointsForDefusingBomb1=0;
                PointsForDefusingBomb2=0;
                PointsForDefusingBomb3=0;
                DefaultWildPassGuessesNumber=3;

            }
            catch (Exception ex)
            {
                Util.ErrorLogging.LogError(ex);
                return;
            }

        }


        /// <summary>
        /// Checks whether or not there is something blocking avatar's way
        /// </summary>
        /// <returns></returns>
        public bool IsFreeToGo()
        {
            //DECLARE
            bool isFreeToGo;
            Util.Classes.Obstacle o=null;
            int[] avatarRect = null;
            bool atLeast1AchievementDone = false;
            
        
            try
            {
                //reset isFreeToGo
                isFreeToGo = true;

                

                #region portal touched?

                //avatar rect
                avatarRect = new int[] { (int)Util.Cache.Avatar.CurrentPosition.X, (int)Util.Cache.Avatar.CurrentPosition.Y, Util.Cache.Avatar.Width, Util.Cache.Avatar.Height };

                for (int i = 0; i < Levels.LevelManager.CurrentMap.CurrentMapCell.Portals.Count; i++)
                {
                    //see if avatar touched the portal
                   

                    if (Levels.LevelManager.CurrentMap.CurrentMapCell.Portals[i].PortalRectangle.Intersects(new Rectangle(avatarRect[0],avatarRect[1],avatarRect[2],avatarRect[3])))
                    {
                        #region if portal IS END

                        if (Levels.LevelManager.CurrentMap.CurrentMapCell.Portals[i].IsEnd)
                        {
                            //if all letters found and bomb defused

                            if (this.ChosenSecretWord.Word.Length == this.LettersFound && this.BombDefused)
                            {
                                if (!atLeast1AchievementDone)
                                {
                                    #region ALL LETTERS FOUND AND BOMB DEFUSED ?

                                    if (!this.StarsAwardedForAllLetters && !this.StarsAwardedForBombDefused && !this.StarsAwardedForSolvingSecretWord)
                                    {
                                        //add points
                                        Levels.LevelManager.CurrentMap.Points += this.PointsForFindingAllLetters + this.PointsForDefusingBomb;
                                        //save flags
                                        this.StarsAwardedForBombDefused = true;
                                        this.StarsAwardedForAllLetters = true;
                                        this.StarsAwardedForSolvingSecretWord = true;
                                        World.StorageManager.SaveAwardedStarsForTheMap();

                                        //notify user
                                        Util.MessageBox.ShowMessage("You receive " + Levels.LevelManager.CurrentMap.Points.ToString() + " for finding all letters and defusing the bomb! Number of stars: " + World.StorageManager.SavePointsAndStarsForTheMap().ToString());
                                        Util.MessageBox.IsLastMessageBox = true;
                                        atLeast1AchievementDone = true;
                                    }

                                    #endregion
                                }
                            }

                            //if all letters found (bomb not defused)
                            if (this.ChosenSecretWord.Word.Length == this.LettersFound)
                            {
                                if (!atLeast1AchievementDone)
                                {
                                    #region ALL LETTERS FOUND

                                    if (!this.StarsAwardedForAllLetters && !this.StarsAwardedForSolvingSecretWord)
                                    {
                                        Levels.LevelManager.CurrentMap.Points += this.PointsForFindingAllLetters;
                                        //save flags
                                        this.StarsAwardedForAllLetters = true;
                                        this.StarsAwardedForSolvingSecretWord = true;
                                        World.StorageManager.SaveAwardedStarsForTheMap();

                                        //notify user
                                        Util.MessageBox.ShowMessage("You receive " + Levels.LevelManager.CurrentMap.Points.ToString() + " for finding all letters! Number of stars: " + World.StorageManager.SavePointsAndStarsForTheMap().ToString());
                                        Util.MessageBox.IsLastMessageBox = true;
                                        atLeast1AchievementDone = true;
                                    }

                                    #endregion
                                }
                            }

                            //if bomb defused && password guessed (letters found are less than secret word's lenght)
                            if (this.BombDefused && this.ChosenSecretWord.Solved && this.ChosenSecretWord.Word.Length > this.LettersFound)
                            {
                                if (!atLeast1AchievementDone)
                                {
                                    #region BOMB DEFUSED AND SECRET WORD SOLVED
                                    if (!this.StarsAwardedForSolvingSecretWord && !this.StarsAwardedForBombDefused) //BUT NOT ALL LETTERS FOUND because user guessed the password, did not find all the letters
                                    {
                                        //add points
                                        Levels.LevelManager.CurrentMap.Points += this.PointsForGuessingPassword + this.PointsForDefusingBomb;
                                        //save flags
                                        this.StarsAwardedForBombDefused = true;
                                        this.StarsAwardedForSolvingSecretWord = true;
                                        World.StorageManager.SaveAwardedStarsForTheMap();

                                        //notify user
                                        Util.MessageBox.ShowMessage("You receive " + Levels.LevelManager.CurrentMap.Points.ToString() + " for guessing the password and defusing the bomb! Number of stars: " + World.StorageManager.SavePointsAndStarsForTheMap().ToString());
                                        Util.MessageBox.IsLastMessageBox = true;
                                        atLeast1AchievementDone = true;
                                    }


                                    #endregion
                                }
                            }


                            //if password guessed but bomb not defused
                            if (this.ChosenSecretWord.Solved && this.ChosenSecretWord.Word.Length > this.LettersFound)
                            {
                                if (!atLeast1AchievementDone)
                                {
                                    #region PASSWORD GUESSED

                                    //add points
                                    Levels.LevelManager.CurrentMap.Points += this.PointsForGuessingPassword;
                                    //save flags
                                    this.StarsAwardedForSolvingSecretWord = true;
                                    World.StorageManager.SaveAwardedStarsForTheMap();

                                    //notify user
                                    Util.MessageBox.ShowMessage("You receive " + Levels.LevelManager.CurrentMap.Points.ToString() + " for guessing the password! Number of stars: " + World.StorageManager.SavePointsAndStarsForTheMap().ToString());
                                    Util.MessageBox.IsLastMessageBox = true;
                                    atLeast1AchievementDone = true;

                                    #endregion
                                }
                            }


                            if (!atLeast1AchievementDone)
                            {
                                //notify user
                                Util.MessageBox.ShowMessage("You receive: " + World.StorageManager.SavePointsAndStarsForTheMap().ToString() + " stars. Come back and get all 3!");
                                Util.MessageBox.IsLastMessageBox = true;
                                World.StorageManager.SaveAwardedStarsForTheMap();
                                World.StorageManager.SavePointsAndStarsForTheMap();
                            }
                        #endregion

                        }
                        else
                        {
                            //else just move avatar to the next cell
                            Levels.LevelManager.SetCurrentMapCell(Levels.LevelManager.CurrentMap.CurrentMapCell.Portals[i]);
                            avatarRect = null;
                        }


                        return false;
                    }


                }

              

                #endregion

                #region bomb to draw touched?

                for (int bi = 0; bi < this.CurrentMapCell.Bombs.Count; bi++)
                {
                

                    //see if bomb is on avatar's move path, if not do not check it
                    if (!IsObstacleOnMovePath(new int[] { (int)this.CurrentMapCell.Bombs[bi].Position.X, (int)this.CurrentMapCell.Bombs[bi].Position.Y, this.CurrentMapCell.Bombs[bi].Dimensions[0],this.CurrentMapCell.Bombs[bi].Dimensions[1] }))
                    {
                        continue;
                    }

                    //check it
                    if (World.WorldManager.IsObstacleTouched(new int[] { (int)this.CurrentMapCell.Bombs[bi].Position.X, (int)this.CurrentMapCell.Bombs[bi].Position.Y, this.CurrentMapCell.Bombs[bi].Dimensions[0], this.CurrentMapCell.Bombs[bi].Dimensions[1]}, new int[] { (int)Util.Cache.Avatar.FuturePosition.X, (int)Util.Cache.Avatar.FuturePosition.Y, Util.Cache.Avatar.Width, Util.Cache.Avatar.Height }))
                    {
                        return false;
                    }
                  

                    //if (new Rectangle((int)this.CurrentMapCell.Bombs[bi].Position.X, (int)this.CurrentMapCell.Bombs[bi].Position.Y, this.CurrentMapCell.Bombs[bi].Dimensions[0], this.CurrentMapCell.Bombs[bi].Dimensions[1]).Intersects(new Rectangle((int)Util.Cache.Avatar.FuturePosition.X, (int)Util.Cache.Avatar.FuturePosition.Y, Util.Cache.Avatar.Width, Util.Cache.Avatar.Height)))
                    //{
                    //    return false;
                    //}
                }

                #endregion

                //clear avatar rect
                avatarRect = null;

                if (this.CurrentMapCell.Obstacles != null)
                {
                   
                        for (int oCt = 0; oCt < this.CurrentMapCell.Obstacles.Count;oCt++)
                        {
                            ////get obstacle's instance
                            //o = Levels.LevelManager.GetObstacleByName(this.CurrentMapCell.Obstacles[oCt].ObstacleID);

                            //check if the obstacle is on avatar's way, if no,then do not detect if avatar is free to go
                            if (!IsObstacleOnMovePath(new int[] { (int)this.CurrentMapCell.Obstacles[oCt].Position.X, (int)this.CurrentMapCell.Obstacles[oCt].Position.Y, this.CurrentMapCell.Obstacles[oCt].Dimensions[0], this.CurrentMapCell.Obstacles[oCt].Dimensions[1]}))
                            {
                                continue;
                            }

                            //if obstacle is touched, return true
                            if (World.WorldManager.IsObstacleTouched(new int[]{(int)this.CurrentMapCell.Obstacles[oCt].Position.X,(int)this.CurrentMapCell.Obstacles[oCt].Position.Y,this.CurrentMapCell.Obstacles[oCt].Dimensions[0],this.CurrentMapCell.Obstacles[oCt].Dimensions[1]},new int[]{(int)Util.Cache.Avatar.FuturePosition.X,(int)Util.Cache.Avatar.FuturePosition.Y,Util.Cache.Avatar.Width,Util.Cache.Avatar.Height}))
                            {
                                //if obstacle is touched and is not walkable,then do not allow avatar to walk on it
                                if (!this.CurrentMapCell.Obstacles[oCt].Walkable)
                                {
                                    isFreeToGo = false;


                                #region is landmine
                                    if (this.CurrentMapCell.Obstacles[oCt].Type == Util.Helpers.PublicEnums.ObstacleType.StaticHarm)
                                    {
                                        //I know that only certain obstacles are being processed here. Obstacles like landmine
                                        if (this.CurrentMapCell.Obstacles[oCt].ObstacleID.ToLower().Contains("landmine"))
                                        {
                                            o = LevelManager.GetObstacleByName(this.CurrentMapCell.Obstacles[oCt].ObstacleID);

                                            if (o != null)
                                            {
                                                //if there is onLandmineTriggered event, rais
                                                //loop through events
                                                for (int eI = 0; eI < o.Events.Count; eI++)
                                                {
                                                    if (o.Events[eI].EventType == Util.Helpers.PublicEnums.EventType.onLandmineStepped)
                                                    {
                                                        //add current obstacle to draw as parameter to the event
                                                        o.Events[eI].Parameters[0].Parameter = this.CurrentMapCell.Obstacles[oCt];//we want to alter that instance of obstacle to draw
                                                        //raise event
                                                        World.EventHandler.RaiseEvent(o.Events[eI]);
                                                        break;
                                                    }
                                                }
                                            }

                                            //clear
                                            o = null;
                                        }
                                    }

                                    break;
                                }

                                    #endregion

                                #region isWater
                                //otherwise, if it's water then make avatar look like he's diving
                                if (this.CurrentMapCell.Obstacles[oCt].IsWater)
                                {
                                    //set previous state (only if not set yet)
                                    if (Util.Cache.Avatar.Position != Util.Helpers.PublicEnums.AvatarPosition.Dive)
                                    {
                                        //not set to dive yet, save previous state
                                        Util.Cache.Avatar.SetPreviousState();
                                    }

                                    //update avatar 
                                    //Util.Cache.Avatar.Position = Util.Helpers.PublicEnums.AvatarPosition.Dive;
                                    //Util.Cache.Avatar.CurrentState = Characters.Avatar.States.Dive;
                                    Util.Cache.Avatar.SetState(Util.Helpers.PublicEnums.AvatarPosition.Dive);

                                    //set obstacle (water obstacle)
                                    World.WorldManager.WaterObstacle = this.CurrentMapCell.Obstacles[oCt].ObstacleID;
                                }
                                #endregion

                            }
                            else
                            {
                                isFreeToGo = true;
                                //break;

                                //if is water, and no more touching the water=no more diving
                                //get out of the water!
                                if (this.CurrentMapCell.Obstacles[oCt].IsWater)
                                {
                                    //if player was diving, then stop taking away breath/health
                                    if (Util.Cache.Avatar.Position == Util.Helpers.PublicEnums.AvatarPosition.Dive)
                                    {
                                        if (World.WorldManager.WaterObstacle != null)
                                        {
                                            //if this is an obstacle that represents water eg. pond, then set
                                            //avatar's position to regular, not diving anymore
                                            if (this.CurrentMapCell.Obstacles[oCt].ObstacleID == World.WorldManager.WaterObstacle)
                                            {
                                                //sometimes, the result of obstacle touched is not correct.
                                                //it happens in case of diving
                                                //thah is why, check twice before getting avatar out of the water
                                                AvatarOutOfTheWaterCounter += 1;//increment by 1

                                                //if twice the same result, then gtet avatar out of the water
                                                if (AvatarOutOfTheWaterCounter ==1)
                                                {
                                                    Util.Cache.Avatar.Position = Util.Cache.Avatar.PreviousPosition;
                                                    Util.Cache.Avatar.DetermineAndSetWalkDirection();
                                                    World.WorldManager.WaterObstacle = null;

                                                    //reset
                                                    AvatarOutOfTheWaterCounter = 0;
                                                }
                                            }
                                        }

                                    }
                                }

                            }

                        }
                    
                }

                return isFreeToGo;
            }
            catch (Exception ex)
            {
                Util.ErrorLogging.LogError(ex);
                return false;
            }
        }

        /// <summary>
        /// check if specified obstacle
        /// is on the move path
        /// </summary>
        /// <param name="otd"></param>
        /// <returns></returns>
        public bool IsObstacleOnMovePath(int[] objectData)
        {
            try
            {
                //foreach (Characters.Helpers.MovePatternCheckpoint movePoint in Util.Cache.Avatar.MovePoints)
                for(int i=0;i<Util.Cache.Avatar.MovePoints.Count;i++)
                {


                    if (Util.Cache.Avatar.MovePoints[i].Pos.Intersects(new Rectangle(objectData[0], objectData[1], objectData[2], objectData[3])))
                    {
                        return true;
                    }
                }


                return false;

            }
            catch (Exception ex)
            {
                Util.ErrorLogging.LogError(ex);
                return false;
            }
        }

        /// <summary>
        /// checks if specified avatar pos will collide with
        /// any static no harm obstalce(only those that are blocking the path, skip mines, water etc.)
        /// </summary>
        /// <returns></returns>
        public bool IsFreeToGoSimulation(int[] avatarPos)
        {
            //DECLARE
            bool isFreeToGo = true;
           

            try
            {
                if (this.CurrentMapCell.Obstacles != null)
                {
                    if (this.CurrentMapCell.Obstacles.Count > 0)
                    {
                        for (int i = 0; i < this.CurrentMapCell.Obstacles.Count;i++)
                        {
                            

                            if (!this.CurrentMapCell.Obstacles[i].Walkable && (this.CurrentMapCell.Obstacles[i].Type == Util.Helpers.PublicEnums.ObstacleType.StaticNoHarm || this.CurrentMapCell.Obstacles[i].Type == Util.Helpers.PublicEnums.ObstacleType.StaticNoHarmInteractive))
                            {

                                //if obstacle is touched, return true
                                if (World.WorldManager.IsObstacleTouched(new int[] { (int)this.CurrentMapCell.Obstacles[i].Position.X, (int)this.CurrentMapCell.Obstacles[i].Position.Y, this.CurrentMapCell.Obstacles[i].Dimensions[0], this.CurrentMapCell.Obstacles[i].Dimensions[1]}, avatarPos)) //COMMENT: IS THERE OFFSET NECESSARY IN THIS CASE?
                                {
                                    //if obstacle is touched and is not walkable,then do not allow avatar to walk on it
                                        isFreeToGo = false;
                                        break;
                                    
                                }
                            }
                        }
                    }
                }




                return isFreeToGo;


            }
            catch (Exception ex)
            {
                Util.ErrorLogging.LogError(ex);
                return false;
            }
        }

        #endregion

        #region drawing

        /// <summary>
        /// Draw the map
        /// </summary>
        public void Draw()
        {
 
            try
            {
                //draw current map cell
                this.CurrentMapCell.DrawCell();


                Util.Cache.GraphicsDevice.Clear(Color.White);



            }
            catch (Exception ex)
            {
                Util.ErrorLogging.LogError(ex);
                return;
            }


        }

        #endregion

    }
}
